/**
 * @file lv_port_indev_templ.c
 *
 */

/*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1

/*********************
 *      INCLUDES
 *********************/
#include "lv_port_indev_template.h"
#include "cst816d.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/

static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t* indev_drv, lv_indev_data_t* data);

/**********************
 *  STATIC VARIABLES
 **********************/
lv_indev_t* indev_touchpad;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

#define CST816D_SCL_PIN 5
#define CST816D_SDA_PIN 4
#define CST816D_RST_PIN 8
#define CST816D_INT_PIN 9

#define I2C_MASTER_TX_BUF_DISABLE 0   /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0   /*!< I2C master doesn't need buffer */
#define ACK_CHECK_EN              0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS             0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL                   0x0 /*!< I2C ack value */
#define NACK_VAL                  0x1 /*!< I2C nack value */

static i2c_port_t i2c_port = I2C_NUM_0;
bool flag = 0;
struct cst816d tp;

static int esp32c3_i2c_read(uint8_t reg, uint8_t* bytes, uint32_t len)
{
	int ret = 0;
	i2c_cmd_handle_t cmd;

	cmd = i2c_cmd_link_create();
	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, CST816D_I2C_7BIT_ADDR << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN);
	i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);

	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, CST816D_I2C_7BIT_ADDR << 1 | I2C_MASTER_READ, ACK_CHECK_EN);
	i2c_master_read(cmd, bytes, len - 1, ACK_VAL);
	i2c_master_read_byte(cmd, bytes + len - 1, NACK_VAL);
	i2c_master_stop(cmd);

	ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_PERIOD_MS);
	if (ret == ESP_ERR_TIMEOUT) {
		printf("CST816D Bus is busy\n");
	} else if (ret != ESP_OK) {
		printf("CST816D Write Failed\n");
	}

	i2c_cmd_link_delete(cmd);

	return ret;
}

static int esp32c3_i2c_write(uint8_t reg, uint8_t* bytes, uint32_t len)
{
	int ret = 0;
	i2c_cmd_handle_t cmd;

	cmd = i2c_cmd_link_create();
	i2c_master_start(cmd);
	i2c_master_write_byte(cmd, CST816D_I2C_7BIT_ADDR << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN);
	i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
	for (int i = 0; i < len; i++) {
		i2c_master_write_byte(cmd, bytes[i], ACK_CHECK_EN);
	}
	i2c_master_stop(cmd);

	ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_PERIOD_MS);
	if (ret == ESP_ERR_TIMEOUT) {
		printf("CST816D Bus is busy\n");
	} else if (ret != ESP_OK) {
		printf("CST816D Write Failed\n");
	}

	i2c_cmd_link_delete(cmd);

	return ret;
}

static void esp32c3_rst_pin(int val)
{
	gpio_set_level(CST816D_RST_PIN, val);
}

static void esp32c3_msleep(int ms)
{
	usleep(1000 * ms);
}

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
	flag = 1;
}

static void board_init()
{
	int ret = 0;

	/* gpio */
	gpio_reset_pin(CST816D_RST_PIN);
	gpio_set_direction(CST816D_RST_PIN, GPIO_MODE_OUTPUT);

	/* interrupt */
	gpio_reset_pin(CST816D_INT_PIN);
	gpio_set_direction(CST816D_INT_PIN, GPIO_MODE_INPUT);
	gpio_set_intr_type(CST816D_INT_PIN, GPIO_INTR_NEGEDGE);
	gpio_install_isr_service(0);
	gpio_isr_handler_add(CST816D_INT_PIN, gpio_isr_handler, (void*)CST816D_INT_PIN);

	/* i2c */
	i2c_config_t conf = {
		.mode = I2C_MODE_MASTER,
		.sda_io_num = CST816D_SDA_PIN,
		.sda_pullup_en = GPIO_PULLUP_ENABLE,
		.scl_io_num = CST816D_SCL_PIN,
		.scl_pullup_en = GPIO_PULLUP_ENABLE,
		.master.clk_speed = 400000,
	};

	i2c_driver_install(i2c_port, I2C_MODE_MASTER, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
	ret = i2c_param_config(i2c_port, &conf);
	if (ret) {
		printf("err: i2c_param_config(%d, conf), ret = %d\n", i2c_port, ret);
	}
}

void lv_port_indev_init(void)
{
	/**
	 * Here you will find example implementation of input devices supported by LittelvGL:
	 *  - Touchpad
	 *  - Mouse (with cursor support)
	 *  - Keypad (supports GUI usage only with key)
	 *  - Encoder (supports GUI usage only with: left, right, push)
	 *  - Button (external buttons to press points on the screen)
	 *
	 *  The `..._read()` function are only examples.
	 *  You should shape them according to your hardware
	 */

	static lv_indev_drv_t indev_drv;

	/*------------------
	 * Touchpad
	 * -----------------*/

	/*Initialize your touchpad if you have*/
	touchpad_init();

	/*Register a touchpad input device*/
	lv_indev_drv_init(&indev_drv);
	indev_drv.type = LV_INDEV_TYPE_POINTER;
	indev_drv.read_cb = touchpad_read;
	indev_touchpad = lv_indev_drv_register(&indev_drv);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/*------------------
 * Touchpad
 * -----------------*/

/*Initialize your touchpad*/
static void touchpad_init(void)
{
	/*Your code comes here*/
	board_init();

	/* set the param */
	tp.cfg.fix.width = 240;
	tp.cfg.fix.height = 280;

	/* register the operation */
	tp.ops.read = esp32c3_i2c_read;
	tp.ops.write = esp32c3_i2c_write;
	tp.ops.msleep = esp32c3_msleep;
	tp.ops.reset = esp32c3_rst_pin;

	/* cst816d touch panel api test */
	cst816d_init(&tp);
}

/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t* indev_drv, lv_indev_data_t* data)
{
	bool pressed = 0;
	uint16_t x = 0;
	uint16_t y = 0;
	static lv_coord_t last_x = 0;
	static lv_coord_t last_y = 0;

	if (flag) {
		flag = 0;
		cst816d_get_coord(&tp, &pressed, &x, &y);
	} else {
		pressed = false;
	}

	/*Save the pressed coordinates and the state*/
	if (pressed) {
		last_x = x;
		last_y = y;
		data->state = LV_INDEV_STATE_PR;
	} else {
		data->state = LV_INDEV_STATE_REL;
	}

	/*Set the last pressed coordinates*/
	data->point.x = last_x;
	data->point.y = last_y;
}

#else /*Enable this file at the top*/

/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
